# Whisper 对接文档（推荐 v2）

本文档用于客户端/模块对接 Whisper 网络校验系统，覆盖：

- v2（推荐）：POST + HMAC-SHA256 + 防重放
- legacy（兼容）：GET + MD5
- 安全文件下发（可选）：RSA + AES-GCM + 流式下载

## 1. 配置参数

- API_BASE_URL：接口根路径（建议以 `/api` 结尾），例如：`http://127.0.0.1:8000/api`
- INSTANCE_ID：实例ID（后台“实例管理”）
- SECRET_KEY：签名密钥（后台“实例管理”）
- ENCRYPT_KEY：可选，若后端开启加密返回则填写（否则留空）

## 2. 通用返回

```json
{
  "code": 0,
  "msg": "success",
  "data": {}
}
```

若开启加密返回：

```json
{
  "code": 0,
  "msg": "success",
  "encrypted_data": "base64...",
  "enc_ver": 2
}
```

客户端要求：

- 有 `encrypted_data` 时优先解密并把解密结果当作 `data` 处理
- `enc_ver=2` 使用 AES-GCM

## 3. v2 签名规则（推荐）

请求：

- METHOD：POST
- PATH：例如 `/api/v2/check`（不含域名、不含 query）
- Header：
  - `X-Timestamp`：秒级时间戳字符串
  - `X-Nonce`：随机字符串（一次性）
  - `X-Signature`：HMAC-SHA256 hex 小写

签名串：

```
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY_SHA256
```

其中 `BODY_SHA256` 为原始 JSON 字节的 sha256(hex)。建议使用紧凑 JSON（无空格）。

## 4. legacy 签名规则（兼容）

接口（示例）：`GET /api/check`

sign 计算：

1. 取所有 query 参数（不含 sign），按参数名 ASCII 升序排序
2. 拼接为 `k1=v1&k2=v2...`
3. 末尾拼接 `SECRET_KEY`
4. MD5(hex) 得到 sign

## 5. 核心接口（卡密）

### 5.1 校验/激活/绑定

- v2：`POST /api/v2/check`
- legacy：`GET /api/check`

请求体（v2）：

```json
{"key":"KEY-...","hwid":"HWID-...","instance_id":"73288705"}
```

### 5.2 主动解绑

- v2：`POST /api/v2/unbind`
- legacy：`GET /api/unbind`

### 5.3 获取软件信息

- v2：`POST /api/v2/info`
- legacy：`GET /api/info`

### 5.4 心跳检测（卡密状态轮询）

- v2：`POST /api/v2/heartbeat`
- legacy：`GET /api/heartbeat`

客户端激活卡密后，定期调用此接口检测卡密是否仍然有效（过期/封禁/机器码不匹配等）。

与 `/check` 不同，心跳接口**不会触发激活、不会绑定/换绑机器码、不写入事件日志**，适合高频轮询。

请求体（v2）：

```json
{"key":"KEY-...","hwid":"HWID-...","instance_id":"73288705"}
```

成功返回额外包含 `remaining_seconds`（剩余秒数，永久卡密为 null）。

卡密未激活时返回 code=7。

### 5.5 云变量/事件日志（legacy）

- `GET /api/cloudvar?key=...`
- `GET /api/log?message=...`

## 6. 安全文件下发（可选）

### 6.1 获取公钥

`GET /api/v2/secure/public_key`

返回：

```json
{"public_key":"-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----"}
```

### 6.2 安全校验（握手）

`POST /api/v2/secure/verify`

Body：

```json
{"encrypted_key":"base64...","encrypted_data":"base64..."}
```

说明：

- encrypted_key：RSA-OAEP 加密后的 AES key（32 bytes）
- encrypted_data：AES-GCM 加密的 JSON（Nonce(12)+Tag(16)+Ciphertext）
- JSON 里必须包含：`card_key`、`instance_id`、`hwid`、`timestamp`
- timestamp 允许误差 120 秒

成功后返回：

```json
{"payload":"base64..."}
```

payload 解密后包含：

- token：一次性下载 token
- download_url：下载地址（含 token query）
- file_key：用于下载流 AES-GCM 解密的 key(hex)
- file_info：版本、大小、checksum

### 6.3 下载

`GET /api/v2/secure/download?token=...`

返回为二进制流：

- Nonce(12) + Ciphertext + Tag(16)

注意：

- token 一次性使用且会过期
- 下载接口对单卡密做限流：1 分钟最多 10 次
- 安全模式只支持 `LuaScript.file_url` 为本地 `/static/...` 路径

## 7. 后台统计口径（今日调用次数）

后台“今日调用/次数请求”统计来源于 `access_logs`，会在以下场景写入：

- 普通校验接口 `/api/check`、`/api/v2/check`
- 安全校验 `/api/v2/secure/verify`（成功命中卡密后）
- 安全下载 `/api/v2/secure/download`（成功开始下载后）
